package com.cngphone.AnFetion;

import java.util.ArrayList;

import android.app.NotificationManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.provider.Contacts;
import android.util.Log;
import android.widget.Toast;

import com.cngphone.AnFetion.Fetion.FetionBase;
import com.cngphone.AnFetion.Fetion.FetionMessageProvider;
import com.cngphone.AnFetion.Fetion.HashedPasswordParameter;
import com.cngphone.AnFetion.Fetion.IStatusChangedReceiver;
import com.cngphone.AnFetion.Fetion.SsiAppSignin;
import com.cngphone.AnFetion.Fetion.StatusDefine;
import com.cngphone.AnFetion.Fetion.SystemConfig;
import com.cngphone.AnFetion.Fetion.TcpSendAndReceive;
import com.cngphone.AnFetion.Fetion.SIPC.GetContactsInfo;
import com.cngphone.AnFetion.Fetion.SIPC.GetFriendInfo;
import com.cngphone.AnFetion.Fetion.SIPC.GetFriendsList;
import com.cngphone.AnFetion.Fetion.SIPC.GetPersonalInfo;
import com.cngphone.AnFetion.Fetion.SIPC.IMAccept;
import com.cngphone.AnFetion.Fetion.SIPC.IMDelete;
import com.cngphone.AnFetion.Fetion.SIPC.IMInvite;
import com.cngphone.AnFetion.Fetion.SIPC.RemotePackageHandler;
import com.cngphone.AnFetion.Fetion.SIPC.ReserveNotify;
import com.cngphone.AnFetion.Fetion.SIPC.SIPCLogin;
import com.cngphone.AnFetion.Fetion.SIPC.SendSMS;
import com.cngphone.AnFetion.Fetion.SIPC.SerialManager;
import com.cngphone.AnFetion.Fetion.SIPC.SetPersonalInfo;
import com.cngphone.AnFetion.Fetion.SIPC.SetPresence;
import com.cngphone.AnFetion.Fetion.TcpSendAndReceive.IConnectionClosedHandler;
import com.cngphone.AnFetion.tools.LogUtil;

public class FetionService extends Service implements IConnectionClosedHandler {

    private static final long updateInterval = 1000 * 120;
    private Handler activeHandler;
    private Runnable hideProgressRunnable = new Runnable() {
		@Override
		public void run() {
			onHideNotifyProgress();
		}
	};
    private static final Handler serviceHandler = new Handler();
    private static final String TAG = "FetionService";

    public static SendSMSActivity chatActivity;
    // Tiger.Add
    public static FetionListFriends fetionListFriends;

    private static FetionService service = null;
    private static TcpSendAndReceive tcpConnection;
    private static WifiManager.WifiLock wifiLock;
    private static GetFriendsList getFriendsList;
    private static FetionFriend me;
    private static boolean logined = false;

    public ArrayList<InitFinishedHandler> initfinished = new ArrayList<InitFinishedHandler>();
    public ArrayList<INotifyProgressHandler> notifyProgress = new ArrayList<INotifyProgressHandler>();

    private final IBinder mBinder = new LocalBinder();

    private SystemConfig config;
    private SsiAppSignin appSignin;
    private HashedPasswordParameter hashedPassword;
    // private ArrayList<FetionFriend> friendsSentNewMessage = new ArrayList<FetionFriend>();
    private ReserveNotify reserveNotify;
    private FriendManager friendManager;
    private GetContactsInfo getContactsInfo;
    private int currentStatus;
    private SerialManager serialManager;
    private String lastError;
    private SIPCLogin login;
    private RemotePackageHandler remotePackageHandler;
    int retryCount = 1;

    private Runnable beginThread = new Runnable() {
        @Override
        public void run() {

            if (!getSystemConfig()) {
                if (lastError.contains("Can't find sipc-proxy")) onInitFinished(false, "请检查用户名是否输入有误！");
                else if (lastError.contains("code is:40")
                        || lastError.contains("authentication challenge is null")) onInitFinished(false,
                        "用户名或密码错误！");
                else onInitFinished(false, "获取系统配置失败！");
                return;
            }
            tcpsipcconnect();
            //super.run();
        }

    };

    private Runnable activeRunnable = new Runnable() {

        @Override
        public void run() {
            if (tcpConnection != null && !tcpConnection.isClosing()) {
                try {
                    if (login.MakeActive()) {
                        activeHandler.postAtTime(this, SystemClock.uptimeMillis() + updateInterval);
                        return;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                logined = false;
                Log.i(TAG, "Heartbeat thread is closed because tcp closed.");
            }
        }

    };

    private Runnable retryThread = new Runnable() {
        @Override
        public void run() {
			boolean infiniteReconnect = PreferenceManager
					.getDefaultSharedPreferences(getApplicationContext())
					.getBoolean(FetionPreferenceActivity.INFINITE_RECONNECT,
							false);
            if (infiniteReconnect || retryCount < 4) {
                Intent i = new Intent(Settings.ACTION_RECONNECT);
                i.putExtra("retry", retryCount);
                MiscUtil.getContext(null).sendBroadcast(i);
                if (tcpsipcconnect()) {
                    i = new Intent(Settings.ACTION_RECONNECT_SUCCESS);
                    MiscUtil.getContext(null).sendBroadcast(i);
                    return;
                }
                retryCount++;
                activeHandler.postAtTime(this, SystemClock.uptimeMillis() + 15 * 1000);
            } else {
                Intent i = new Intent(Settings.ACTION_RECONNECT_FAIL);
                MiscUtil.getContext(null).sendBroadcast(i);
            }
            // Toast.makeText(MiscUtil.getContext(FetionService.this), "飞信重新连接失败......."+retryCount,
            // Toast.LENGTH_SHORT).show();
            // super.run();
        }
    };

    private BroadcastReceiver reconnectReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (action.equals(Settings.ACTION_RECONNECT_FAIL)) {
                if (chatActivity != null) {
                    chatActivity.finish();
                }
                if (fetionListFriends != null) {
                    fetionListFriends.isClosing = true;
                    fetionListFriends.finish();
                }
                FetionService.this.close();
                MiscUtil.notifyDisconnect(context, "飞信重新连接失败.......", true);
                // Toast
                // .makeText(MiscUtil.getContext(FetionService.this), "飞信重新连接失败.......",
                // Toast.LENGTH_SHORT).show();
            } else if (action.equals(Settings.ACTION_RECONNECT_SUCCESS)) {
                // MiscUtil.notifyDisconnect(context, "飞信重新连接成功", false);
                MiscUtil.cancelNotification(context, MiscUtil.DIS_CONNECT_NOTIFY_ID);
                Toast.makeText(MiscUtil.getContext(FetionService.this), "飞信重新连接成功", Toast.LENGTH_SHORT)
                        .show();
            } else if (action.equals(Settings.ACTION_RECONNECT_CLEAR)) {
                MiscUtil.cancelNotification(context, MiscUtil.DIS_CONNECT_NOTIFY_ID);
            } else {
                int times = intent.getIntExtra("retry", 1);
                MiscUtil.notifyDisconnect(context, "飞信断线, 正在重新连接.......第 " + times + " 次", false);
                // Toast.makeText(MiscUtil.getContext(FetionService.this), "飞信断线, 正在重新连接......." +
                // times,
                // Toast.LENGTH_SHORT).show();
            }
        }

    };

    private IStatusChangedReceiver statusChangedReceiver = new IStatusChangedReceiver() {

        @Override
        public boolean FetionSetErrorText(String text) {
            setLastError(text);
            return true;
        }

        @Override
        public boolean FetionStatusChanged(int oldStatus, int newStatus) {
            currentStatus = newStatus;
            FrameWork.synchronizednotify(FetionService.this);
            return true;
        }

    };

    public interface InitFinishedHandler {
        public void initFinished(boolean finishedok, CharSequence errorMessage);
    }

    public class LocalBinder extends Binder {
        public FetionService getService() {
            return FetionService.this;
        }
    }

    public FetionFriend getMyself() {
        return me;
    }

    public boolean isConnected() {
        return tcpConnection != null && !tcpConnection.isClosing();
    }

    public void setFetionListFriends(FetionListFriends fLF) {
        if (fetionListFriends != null && !fetionListFriends.isClosing) fetionListFriends.finish();
        fetionListFriends = fLF;
    }

    public FetionListFriends getFetionListFriends() {
        return fetionListFriends;
    }

    public GetFriendsList getGetFriendsList() {
        return getFriendsList;
    }

    public ArrayList<INotifyProgressHandler> getNotifyProgress() {
        return notifyProgress;
    }

    public ReserveNotify getReserveNotify() {
        return reserveNotify;
    }

    public int getCurrentStatus() {
        return currentStatus;
    }

    public void setLastError(String lastError) {
        this.lastError = lastError;
        if (Settings.DEBUG) Log.d("lastError", lastError);
    }

    public String getLastError() {
        return lastError;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    public void addInitFinishedHandler(InitFinishedHandler obj) {
        this.initfinished.add(obj);
    }

    public void removeInitFinishedHandler(InitFinishedHandler obj) {
        this.initfinished.remove(obj);
    }

    private void onInitFinished(boolean result, CharSequence errorMessage) {
        if (!result) {
        	serviceHandler.post(hideProgressRunnable);
        }
        for (int i = 0; i < initfinished.size(); i++) {
            this.initfinished.get(i).initFinished(result, errorMessage);
        }
    }

    public void begin() {
        initliaze();
        activeHandler.post(beginThread);
    }

    public boolean tcpsipcconnect() {
        if (!connectAndLogin()) {
            onInitFinished(false, "登录失败");
            return false;
        }
        if (!getFriends()) {
            onInitFinished(false, "获取好友列表失败");
            return false;
        }
        onInitFinished(true, "");
        logined = true;
        reserveNotify();
        getContactsInfo();
        return true;
    }

    private void initliaze() {
        config = new SystemConfig(Settings.loginName);
        hashedPassword = FetionBase.HashPassword(Settings.loginPass);
    }

    private boolean getSystemConfig() {
        config.addStatusChangedReceiver(statusChangedReceiver);

        serviceHandler.post(new Runnable() {
			
			@Override
			public void run() {
		        onShowNotifyProgress("请稍候", "正在登陆");
				
			}
		});
        // onShowNotifyProgress("请稍候", "正在获取配置信息");
        boolean workresult = config.execute();
        // onHideNotifyProgress();
        try {
            if (!workresult) {
                return false;
            }
        } finally {
            config.removeStatusChangedReceiver(statusChangedReceiver);
        }
        // onShowNotifyProgress("请稍候", "正在获取用户信息");

        appSignin = new SsiAppSignin(config.getSsiAppSigninUrl(), config.getMobileno(),
                hashedPassword.passwordDigest);
        try {
            appSignin.addStatusChangedReceiver(statusChangedReceiver);
            workresult = appSignin.execute();
            // onHideNotifyProgress();
            if (!workresult) {
                return false;
            }
        } finally {
            appSignin.removeStatusChangedReceiver(statusChangedReceiver);
        }
        Settings.ssic = appSignin.getSsicCookie();
        return true;
    }

    public boolean connectAndLogin() {
        // 连接远程服务器
        // onShowNotifyProgress("请稍候", "正在登录");
        if (tcpConnection != null) tcpConnection.close();
        tcpConnection = TcpSendAndReceive.getInstance(config.getSipcproxy_address(), config
                .getSipcproxy_port());
        tcpConnection.addStatusChangedReceiver(statusChangedReceiver);
        boolean workresult = tcpConnection.execute();

        // onHideNotifyProgress();
        try {
            if (!workresult) {
                return false;
            }
        } finally {
            tcpConnection.removeStatusChangedReceiver(statusChangedReceiver);
        }
        serialManager = new SerialManager();
        int status = Settings.loginStatus;
        if (me != null) {
            status = me.getOnlineStatusInt();
        }
        login = new SIPCLogin(appSignin.getUri(), tcpConnection, serialManager, hashedPassword,
                Settings.ssic, status);
        login.addStatusChangedReceiver(statusChangedReceiver);
        // onShowNotifyProgress("请稍候", "正在登录");
        workresult = login.execute();
        // onHideNotifyProgress();
        try {
            if (!workresult) {
                return false;
            }
        } finally {
            login.removeStatusChangedReceiver(statusChangedReceiver);
        }

        // Save user name and pwd.
        Settings.updateSetting(this);

        remotePackageHandler = new RemotePackageHandler(FetionService.this, appSignin.getUri(),
                tcpConnection, serialManager);
        tcpConnection.addPackageReceivedQueue(remotePackageHandler);
        tcpConnection.addConnectionClosedNotifier(this);

        // Keep wifi while idle
        wifiLock = ((WifiManager) (FetionService.this).getSystemService(Context.WIFI_SERVICE))
                .createWifiLock("AnFetionWifiLock");
        if (wifiLock != null)
        	wifiLock.acquire();

        // 保持在线线程，每10分钟（从响应头中取）保持在线一次，同时每30秒发送一个1字节0的包，不要求回复。
        // 当前实际是每分钟一次。
        activeHandler.postAtTime(activeRunnable, SystemClock.uptimeMillis() + updateInterval);
        return true;
    }

    public void breath() {
        activeHandler.removeCallbacks(activeRunnable);
        activeHandler.postAtTime(activeRunnable, SystemClock.uptimeMillis() + updateInterval);
    }

    @Override
    public void onDestroy() {
        if (isConnected()) {
            close();
        }
        this.unregisterReceiver(reconnectReceiver);
        service = null;
        super.onDestroy();
    }

    public boolean close() {
        if (null != wifiLock && wifiLock.isHeld()) wifiLock.release();

        activeHandler.removeCallbacks(activeRunnable);
        activeHandler.removeCallbacks(retryThread);

        logined = false;

        ((NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE)).cancelAll();

        try {
            tcpConnection.close();
            tcpConnection = null;
        } catch (Exception e) {
        }

        statusChangedReceiver.FetionSetErrorText("Disconnect with the server2!"); // Tiger. Debug.

        // if (fetionListFriends != null) {
        // fetionListFriends.onDisconnectSrv();
        // statusChangedReceiver.FetionSetErrorText("Stop when forground"); // Tiger. Debug.
        // } else {
        // Stop when background
        statusChangedReceiver.FetionSetErrorText("Stop when background"); // Tiger. Debug。
        // MiscUtil.notifyDisconnect(FetionService.this, -1);
        stopSelf();
        // }

        return true;
    }

    public boolean getFriends() {
    	serviceHandler.post(hideProgressRunnable);
        serviceHandler.post(new Runnable() {
			
			@Override
			public void run() {
		        onShowNotifyProgress("请稍候", "正在获取好友列表");
			}
		});
        int oldstatus = Settings.STATUS_OFFLINE;
        if (me != null) {
            oldstatus = me.getOnlineStatusInt();
        }
        getFriendsList = new GetFriendsList(this, appSignin.getUri(), tcpConnection, serialManager);
        getFriendsList.addStatusChangedReceiver(statusChangedReceiver);
        boolean workresult = getFriendsList.execute();
        // onHideNotifyProgress();
        try {
            if (!workresult) {
            	serviceHandler.post(hideProgressRunnable);
                return false;
            }
        } finally {
            getFriendsList.removeStatusChangedReceiver(statusChangedReceiver);
        }

        ArrayList<FetionFriend> buddies = friendManager.getBuddies();
        for (FetionFriend friend : buddies) {
			friend.setContactName(getContactName(friend.mobile_no));
		}
//        for (FetionFriend ff : buddies) {
//            System.out.println(ff.toFullString());
//        }

        // 我自己
        me = new FetionFriend();
        me.isMe = true;
        // me.Uri = "tel:" + appSignin.getMobileno();
        me.Uri = appSignin.getUri().toString();
        me.mobile_no = appSignin.getMobileno();
        me.IsMobileNo = true;
        me.setFriendName("我自己");
        me.setOnlineStatus(oldstatus == Settings.STATUS_OFFLINE ? Settings.loginStatus : oldstatus);
        me.type = Settings.TYPE_FRIEND;
        // buddies.add(me);

        serviceHandler.post(hideProgressRunnable);

//        for (FetionFriend ff : buddies) {
//            System.out.println(ff.toFullString());
//        }
        
        if (!getPersonalInfo(me)) {
            Log.e(TAG, "Error when parse personalinfo!");
        }

        MiscUtil.notifyFriendChange(this);
        return true;
        // onShowNotifyProgress("请稍候", "正在定制好友状态通知");
    }

	private boolean reserveNotify() {
		boolean workresult;
		reserveNotify = new ReserveNotify(this, appSignin.getUri(), tcpConnection,
                serialManager);
        reserveNotify.addStatusChangedReceiver(statusChangedReceiver);
        workresult = reserveNotify.execute();
        try {
            if (!workresult) {
                return false;
            }
        } finally {
            reserveNotify.removeStatusChangedReceiver(statusChangedReceiver);
        }

        return true;
	}

	private boolean getContactsInfo() {
		boolean workresult;
		getContactsInfo = new GetContactsInfo(this, appSignin.getUri(), tcpConnection, serialManager);
		getContactsInfo.addStatusChangedReceiver(statusChangedReceiver);
        workresult = getContactsInfo.execute();
        try {
            if (!workresult) {
                return false;
            }
        } finally {
        	getContactsInfo.removeStatusChangedReceiver(statusChangedReceiver);
        }

        return true;
	}

	public FriendManager getFriendManager() {
		return friendManager;
	}

	public int sendInvite(FetionFriend info, String nickname) {
        breath();
        IMInvite iminvite = new IMInvite(serialManager, tcpConnection, appSignin.getUri(), info, nickname);
        if (iminvite.execute()) {
            return iminvite.isMobile() ? 2 : 1;
        }
        return 0;
    }

    public boolean setPresence(int newstatus) {
        breath();
        SetPresence sp = new SetPresence(serialManager, tcpConnection, appSignin.getUri(), newstatus);
        return sp.execute();
    }

    public boolean sendAccept(FetionUri uri, boolean accept, String nickname) {
        breath();
        IMAccept imaccept = new IMAccept(serialManager, tcpConnection, appSignin.getUri(), uri, accept,
                nickname);
        return imaccept.execute();
    }

    public int sendSMS(String sendto, String message) {
        breath();
        SendSMS sendsms = new SendSMS(serialManager, tcpConnection, appSignin.getUri(), sendto, message);
        if (!sendsms.execute()) return -1;
        return sendsms.status;
    }

    public boolean setPersonalInfo(String nick, String impresa) {
        breath();
        SetPersonalInfo spi = new SetPersonalInfo(serialManager, tcpConnection, appSignin.getUri(), nick,
                impresa);
        return spi.execute();
    }

    public boolean getPersonalInfo(FetionFriend ff) {
        GetPersonalInfo gpi = new GetPersonalInfo(serialManager, tcpConnection, appSignin.getUri(), ff);
        return gpi.execute();
    }

    public boolean getFriendInfo(FetionFriend ff) {
        GetFriendInfo gfi = new GetFriendInfo(serialManager, tcpConnection, appSignin.getUri(), ff);
        return gfi.execute();
    }

    public void addNotifyProgress(INotifyProgressHandler obj) {
        notifyProgress.add(obj);
    }

    public void removeNotifyProgress(INotifyProgressHandler obj) {
        notifyProgress.remove(obj);
    }

    private void onShowNotifyProgress(String title, String text) {
        for (int i = 0; i < notifyProgress.size(); i++)
            notifyProgress.get(i).showNotifyProgress(title, text);
    }

    private void onHideNotifyProgress() {
        for (int i = 0; i < notifyProgress.size(); i++)
            notifyProgress.get(i).hideNotifyProgress();
    }

    public interface INotifyProgressHandler {
        public void showNotifyProgress(String title, String text);

        public void hideNotifyProgress();
    }

    @Override
    public boolean ConnectionClosed(TcpSendAndReceive sender) {
        tcpConnection.removeConnectionClosedNotifier(this);
        currentStatus = StatusDefine.EMPTY;
        logined = false;
        activeHandler.removeCallbacks(activeRunnable);
        activeHandler.removeCallbacks(retryThread);
        activeHandler.postAtTime(retryThread, SystemClock.uptimeMillis() + 1 * 1000);
        return true;
    }

    /*
     * save the rcvd message
     */
    public void saveMessage(String from, String to, String msg) {
        ContentValues values = new ContentValues();
        values.put(FetionMessageProvider.KEY_FROM, from);
        values.put(FetionMessageProvider.KEY_TO, to);
        values.put(FetionMessageProvider.KEY_MSG, msg);
        getContentResolver().insert(FetionMessageProvider.Fetion.CONTENT_URI, values);
    }

    public boolean sendDelete(FetionUri fetionUri, boolean ban) {
        IMDelete imdelete = new IMDelete(serialManager, tcpConnection, appSignin.getUri(), fetionUri, ban);
        return imdelete.execute();
    }

    @Override
    public void onCreate() {
    	LogUtil.initSysName("AnFetion");
    	
        IntentFilter filter = new IntentFilter(Settings.ACTION_RECONNECT);
        filter.addAction(Settings.ACTION_RECONNECT_FAIL);
        filter.addAction(Settings.ACTION_RECONNECT_SUCCESS);
        filter.addAction(Settings.ACTION_RECONNECT_CLEAR);
        this.registerReceiver(reconnectReceiver, filter);
        service = this;
        HandlerThread networkThread = new HandlerThread("AnfetionNetworkThread");
        networkThread.start();
        activeHandler = new Handler(networkThread.getLooper());
    	friendManager = new FriendManager(this);
    	super.onCreate();
    }

    public static boolean isLogined() {
        return logined;
    }
    
    public static FetionService getService() {
        return service;
    }

    public String getContactName(String mobile_no) {
    	LogUtil.d(TAG, "getContactName:" + mobile_no);
    	if (mobile_no == null) return null;
		Uri phoneUri = Uri.withAppendedPath(Contacts.Phones.CONTENT_FILTER_URL, mobile_no);
		ContentResolver cr = getContentResolver();
		Cursor cursor = cr.query(phoneUri, null, null, null, Contacts.Phones.DEFAULT_SORT_ORDER);
		String name = null;
		if (cursor.moveToFirst()) {
			int nameColumn = cursor.getColumnIndex(Contacts.Phones.NAME);
			name = cursor.getString(nameColumn);
	    	LogUtil.d(TAG, "contactname:" + name);
			return name;
		}
    	return name;
    }


}
